package com.mrd.redis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;


@Component("redisClusterClient")
public class JedisClientClusterImpl {
    private static String redisCode = "utf-8";

    @Autowired
    RedisTemplate redisTemplate;

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

  /* ----------- common --------- */

    /**
     * 根据表达式查询key
     *
     * @param pattern 索引
     * @return Collection
     */
    public Collection keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 根据key删除缓存数据
     *
     * @param key 索引
     * @author han
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 根据索引的集合删除数据
     *
     * @param key 集合索引Collection
     * @author han
     */
    public void delete(Collection key) {
        redisTemplate.delete(key);
    }

  /* ----------- string --------- */

    /**
     * 根据key查询字符串
     *
     * @param key   索引关键字
     * @param clazz 返回的对象
     */
    public <T> T get(String key, Class clazz) {
        String value = (String) redisTemplate.opsForValue().get(key);
        return parseJson(value, clazz);
    }

    /**
     * 根据多个查询索引查询
     *
     * @param keys  多个索引集合
     * @param clazz 返回的对象类型
     * @author han
     */
    public List mget(Collection keys, Class clazz) {
        List values = redisTemplate.opsForValue().multiGet(keys);
        return parseJsonList(values, clazz);
    }

    /**
     * 设置字符对象
     *
     * @param key     索引
     * @param obj     值对象
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public <T> void set(String key, T obj, Long timeout, TimeUnit unit) {
        try {
            if (obj == null) {
                return;
            }

            String value = toJson(obj);
            if (timeout != null) {
                redisTemplate.opsForValue().set(key, value, timeout, unit);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 根据索引查询，并且更新对象
     *
     * @param key   索引
     * @param obj   入参对象
     * @param clazz Class
     * @return T 返回对象
     * @author han
     */
    public <T> T getAndSet(String key, T obj, Class clazz) {
        if (obj == null) {
            return get(key, clazz);
        }

        String value = (String) redisTemplate.opsForValue().getAndSet(key, toJson(obj));
        return parseJson(value, clazz);
    }

    public int decrement(String key, int delta) {
        Long value = redisTemplate.opsForValue().increment(key, -delta);
        return value.intValue();
    }

    public int increment(String key, int delta) {
        Long value = redisTemplate.opsForValue().increment(key, delta);
        return value.intValue();
    }

    /* ----------- list --------- */
    public int size(String key) {
        return redisTemplate.opsForList().size(key).intValue();
    }

    public List range(String key, long start, long end, Class clazz) {
        List list = redisTemplate.opsForList().range(key, start, end);
        return parseJsonList(list, clazz);
    }

    public void rightPushAll(String key, Collection<?> values, Long timeout, TimeUnit unit) {
        if (values == null || values.isEmpty()) {
            return;
        }

        redisTemplate.opsForList().rightPushAll(key, toJsonList(values));
        if (timeout != null) {
            redisTemplate.expire(key, timeout, unit);
        }
    }

    public <T> void leftPush(String key, T obj) {
        if (obj == null) {
            return;
        }

        redisTemplate.opsForList().leftPush(key, toJson(obj));
    }

    public <T> T leftPop(String key, Class clazz) {
        String value = (String) redisTemplate.opsForList().leftPop(key);
        return parseJson(value, clazz);
    }

    public <T> T rightPop(String key, Class clazz) {
        String value = (String) redisTemplate.opsForList().rightPop(key);
        return parseJson(value, clazz);
    }

    public void remove(String key, int count, Object obj) {
        if (obj == null) {
            return;
        }

        redisTemplate.opsForList().remove(key, count, toJson(obj));
    }

    /* ----------- zset --------- */
    public int zcard(String key) {
        return redisTemplate.opsForZSet().zCard(key).intValue();
    }

    public List zrange(String key, long start, long end, Class clazz) {
        Set set = redisTemplate.opsForZSet().range(key, start, end);
        return parseJsonList(setToList(set), clazz);
    }

    private List setToList(Set set) {
        if (set == null) {
            return null;
        }
        return new ArrayList(set);
    }

    public void zadd(String key, Object obj, double score) {
        if (obj == null) {
            return;
        }
        redisTemplate.opsForZSet().add(key, toJson(obj), score);
    }

    public void zrem(String key, Object obj) {
        if (obj == null) {
            return;
        }
        redisTemplate.opsForZSet().remove(key, toJson(obj));
    }

    public void unionStore(String destKey, Collection keys, Long timeout, TimeUnit unit) {
        if (keys == null || keys.isEmpty()) {
            return;
        }

        Object[] keyArr = keys.toArray();
        String key = (String) keyArr[0];

        Collection otherKeys = new ArrayList(keys.size() - 1);
        for (int i = 1; i < keyArr.length; i++) {
            otherKeys.add((String) keyArr[i]);
        }

        redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
        if (timeout != null) {
            redisTemplate.expire(destKey, timeout, unit);
        }
    }

    /* ----------- tool methods --------- */
    public String toJson(Object obj) {
        return JSON.toJSONString(obj, SerializerFeature.SortField);
    }

  public <T> T parseJson(String json, Class clazz) {
    return (T) JSON.parseObject(json, clazz);
  }

  public List toJsonList(Collection<?> values) {
    if (values == null) {
      return null;
    }
    List result = new ArrayList();
    for (Object obj : values) {
      result.add(toJson(obj));
    }
    return result;
  }

  public List parseJsonList(List<String> list, Class clazz) {
    if (list == null) {
      return null;
    }
    List result = new ArrayList();
    for (String s : list) {
      result.add(parseJson(s, clazz));
    }
    return result;
  }

  /**
   * 根据正则表达式删除缓存数据
   *
   * @author han
   * @param keyPreg
   */
  public void delKeys(String keyPreg) {
    Set<String> keys = redisTemplate.keys(keyPreg);
    redisTemplate.delete(keys);
  }

  /**
   * 入队列
   *
   * @author han
   * @param key
   * @param obj
   * @return
   */
  public long lpush(final String key, Object obj) {
    final String value = toJson(obj);
    long result = (long) redisTemplate.execute(new RedisCallback<Long>() {
      @Override
      public Long doInRedis(RedisConnection connection) throws DataAccessException {
        RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
        long count = connection.rPush(serializer.serialize(key), serializer.serialize(value));
        return count;
      }
    });
    return result;
  }

}
